Picture Window Listbox Tutorial

 

Most of you will be familiar with the Listbox control in dialogs, however this tutorial deals with creating a similar control, which is visually customisable using the /draw* commands, available in mIRC.  The example I intend to use for the purpose of this tutorial is a genre list, reading from a stored file in the mIRC directory.

 

In order to make a usable listing system, the following tasks need to be undertaken.

 

1.                   A primary buffering system.

2.                   A functional scrollbar.

3.                   A basic listbox GUI.

4.                   A listing process.

5.                   Event handling (sclick, dclick etc).

 

This tutorial will systematically guide you through these tasks with worked examples.

 

The Primary Buffering System

 

The object of creating a buffering system is that it allows you to manage lists more efficiently, for example adding or deleting lines of data.  It also makes the listing system relatively error free, and quick.

 

Depending on the information you wish to list, the buffering process is slightly different.  This tutorial will demonstrate two different types.  The first is fairly straight forward, and retrieves lines of text from a saved file in the mIRC directory.  The second creates a buffer of filenames from a directory, much in the same way as a mp3 play list would work.

 

Below is an example of buffering from a saved text file, this utilises the file handling routines introduced into mIRC 6.1x

 

alias load.buffer {

  ;check to ensure that the file reference isn’t already open…

  if (!$fopen(buffer)) {

  ;open the genre text file, to the file reference “buffer”…

    .fopen buffer $+(",$scriptdir,genre.txt,")

  }

  ;create an @window list (-h = hidden window, -l = listbox window)…

  window -hl @buffer -1 -1 400 300

  ;clear buffer window to prevent appending text to the end of the listbox…

  clear @buffer

  ;declare variables needed for loop…

  var %x,%i = 0

;initiate the while loop – (!$feof) causes the loop to continue until the end of the text file is reached…

  while (!$feof) {

    inc %i

  ;$fread(buffer) returns the next line of the text file…

    %x = $fread(buffer)

  ;if the next line in the text file exists, write it to the buffer window…

    if (%x) iline @buffer %i %x

  }

;all files must be closed, after use in order to allow them to be used externally of mIRC.

  .fclose buffer

}

 

This should create a buffer window, with a list of music genre types, as shown below: -

 

This window can now be used to write text to a picture window.

 

An important identifier to consider is $line(<window>,N), using this identifier, the text can be retrieved from the Nth line of the buffer window.

 

Try this, type //echo -s $line(@buffer,35) in the active “status window”.

The word “Acid” should echo in the “status window”.

 

It is also possible to determine the number of lines of text in the buffer window using the same identifier, this will be important when creating a scrollbar.

 

Try this, type //echo -s $line(@buffer,0) in the active “status window”.

The number “126” should echo in the “status window”.            

 

 

The second buffering example is one where filenames from a certain directory are listed, this is done using the following buffer alias.

 

alias dbuffer {

  ;create an @window list (-h = hidden window, -l = listbox window)…

  window -hl @buffer -1 -1 400 300

  ;clear buffer window to prevent appending text to the end of the listbox…

  clear @buffer

  ;locate *.mp3 files in your selected directory, and write to the @buffer window…

  var %n = $findfile($sdir(C:\,Select mp3 Directory),*.mp3,@buffer)

}

 

 

You can use the $findfile identifier, to write the file paths to a temporary file (@temp), and then /filter from that to the @buffer window.  However, for the sake of simplicity, this step was omitted.

 

The result of the above alias should be similar to that of the first example.

 

 

Before continuing to the next stage of this tutorial, ensure that you understand the importance of this buffering stage, and are competent at creating a listing buffer.

 

 

A Functional Scrollbar

 

This is probably the most challenging aspect of creating a picture window listbox.  It does require a fair amount of arithmetic, but is fairly straightforward.  The advantage of the identifier below is that it can be manipulated to work on any picture window, with no restrictions on the number of scrollbars you can have on the window in question.

 

 

;$scrollbar(<@win>,<x>,<y>,<w>,<h>,<bg>,<fg>,<fg hilite>,<fg shadow>,<buffer @win>,<MAX lines in list>)

 

The identifier requires 11 parameters, which MUST be assigned.

 

$1 =     Name of Picture Window (Syntax = @name).

$2 =     x co-ordinate.

$3 =     y co-ordinate.

$4 =     w co-ordinate (width).

$5 =     h co-ordinate (height).

$6 =     Background Colour.

$7 =     Foreground Colour.

$8 =     Foreground Highlight.

$9 =     Foreground Shadow.

$10 =    Name of Buffer Window, Linked With Listbox.

$11 =    Max Lines To Display In Listbox.

 

;descriptive breakdown of $scrollbar(…) identifier, and relevant commands.

 

alias scrollbar {

  ;if alias is not called as an identifier return…

  if (!$isid) { return }

  ;else continue to process…

  else {

  ;declare variable for while loop…

    var %i = 1

  ;draw a rectangle according to co-ordinates and colour data provided in identifier, to act as background…

    drawrect -rnf $1 $6 1 $2 $3 $4 $5

  ;initiate while loop, check if hashed data exists for %i…

    while ($hget(scrollbar. $+ $1,%i)) inc %i

  ;if hash data for %i does not exist, add it…

    hadd -m scrollbar. $+ $1 %i $+($2,/,$3,/,$4,/,$5,/,$6,/,$7,/,$8,/,$9,/,$10,/,$11)

  }

}

 

;Scrollbar Mouse Events

 

  ;window name is not specified, * acts as a wildcard window name…

menu * {

  mouse: {

  ;ensure that mouse event is only triggered when the left mouse button is clicked…

    if ($mouse.key & 1) {

  ;declare variable for loop…

      var %r = 1

  ;while loop is set to search through saved hash data for active window…

      while ($hget(scrollbar. $+ $active,%r)) {

  ;tokenize assigns each saved parameter, to an $N identifier, for example $1,$2,$3…

        tokenize 47 $ifmatch

  ;check if the mouse cursor is over the saved co-ordinates for scrollbar, if it is, make it scroll…

        if ($inrect($mouse.x,$mouse.y,$1,$2,$3,$4)) { scroll.update %r $ceil($calc((($mouse.y - $2) / $4) * 100)) }

        inc %r

      }

    }

  }

  ;similar process to above, however this is for the on sclick event…

  sclick: {

    var %r = 1

    while ($hget(scrollbar. $+ $active,%r)) {

      tokenize 47 $ifmatch

      if ($inrect($mouse.x,$mouse.y,$1,$2,$3,$4)) { scroll.update %r $ceil($calc((($mouse.y - $2) / $4) * 100)) }

      inc %r

    }

  }

}

 

This alias assigns your commands for the scrollbar.

 

alias scroll.update {

  ;if the active window is “name of your picture window” continue…

  if ($active == @test) {

  ;if scrollbar id is “id of scrollbar” draw the scroll thumb and run command…

    if ($1 == 1) { draw.scroll @test $1 $2 | echo 8 Scroll One: $2 }

    if ($1 == 2) { draw.scroll @test $1 $2 | echo 4 Scroll Two: $2 }

  }

  if ($active == @test1) {

    if ($1 == 1) { draw.scroll @test1 $1 $2 | draw.buffer }

  }

}

 

 

The alias below, is very important.  It calculates and draws the position of the scrollbar thumb, according to the data saved in hash for the specific scrollbar.

 

alias draw.scroll {

  ;declare variables of scrollbar data

  var %win = $1,%name = $2,%pos = $3

  ;tokenize specific hash data for scrollbar and assign parameters to $N identifiers…

  tokenize 47 $hget(scrollbar. $+ %win,%name)

  ;calculate size of scrollbar thumb, according to amount of data to be listed, ensure there is a minimum thumb size…

  var %sep = $calc($iif($line($9,0) <= $10 ,1,$iif($calc($10 / $line($9,0)) < 0.05,0.05,$calc($10/ $line($9,0)))))

  ;declare variable for thumb shadow colour…

  var %sh = $calc($8 + $rgb(20,20,20))

  ;declare variables for thumb co-ordinates…

  var %x $1, %y $calc(((%pos / 100 * $4) * $calc(1 - %sep)) + $2), %w $3, %h $ceil($calc($4 * %sep))

  ;draw a rectangle over background, so thumb position can be refreshed…

  drawrect -rnf %win $5 1 $1 $2 $3 $4

  ;draw rectangle to represent thumb on scrollbar…

  drawrect -rnf %win $6 1 %x %y %w %h

  ;add 3d detail onto scrollbar thumb…

  drawrect -rn %win $8 1 %x %y %w %h

  drawline -rnf %win $7 1 $calc(%x + 1) $calc((%y + %h) - 2) $calc(%x + 1) $calc(%y + 1) $calc((%x + %w) - 1) $calc(%y + 1)

  drawline -rnf %win %sh 1 $calc(%x + 1) $calc((%y + %h) - 2) $calc((%x + %w) - 2) $calc((%y + %h) - 2) $calc((%x + %w) - 2) $calc(%y + 1)

  ;drawdot command refreshes the scrollbar as –n flags are used in draw commands…

  drawdot $active

}

 

Below is just a sample alias of how the scrollbar identifier is used…

 

alias test {

  ;free all hash data for active window…

  hfree -w *@test

  ;if picture window is open refresh it…

  if ($window(@test)) { close -@ @test | window -ak0hdBpfo +d @test -1 -1 65 160 }

  ;if not open it…

  else { window -ak0hdBpfo +d @test -1 -1 65 160 }

  ;clear the picture window…

  clear @test

  ;draw a rectangle for the window background…

  drawrect -rf @test $rgb(face) 1 0 0 65 160

  ;draw two scrollbars with the custom identifier…

  $scrollbar(@test,10,10,15,140,$rgb(243,240,234),$rgb(233,230,224),$rgb(255,255,255),$rgb(200,200,200),@buffer,5)

  $scrollbar(@test,40,10,15,140,$rgb(243,240,234),$rgb(233,230,224),$rgb(255,255,255),$rgb(200,200,200),@buffer,15)

}

 

The result of the above command is shown below…

 

The scrollbars should be fully functional, i.e. mouse events should work.

 

As you scroll on them, they should echo the command that has been assigned to them, in the status window.

 

The thumbs sizes are different because of the Parameter <Max lines to be listed> in the $scrollbar identifier, see above, those values have been highlighted.

 

That completes the section on the scrollbar.  At this stage you should understand and be able to use the $scrollbar(…) identifier.  Please bare in mind that this identifier is rather basic, and does not include scroll up/down buttons.  Those should be a fairly easy addition if you are competent at using picture windows.  The point of the tutorial was to demonstrate the use of a scrollbar in a picture window list box, and this certainly achieves that goal.

 

Feel free to use this identifier for other purposes as it does not necessarily have to be used for list boxes.

 

 

Basic Listbox GUI

 

The design of the listbox is something that you can decide for yourself, however the one below should suffice for this tutorial.  Please remember that the main advantage of using picture windows over dialog controls is that, the design is completely customisable that includes colours/fonts/shapes etc

 

So be adventurous, I’ve tried to keep this simple for the purpose of this tutorial.

 

 

The following alias, will create a window called @test1, onto which a listbox will be drawn…

 

 

alias test1 {

  ;delete any relevant hash tables for new window…

  hfree -w *@test1

  ;if the window is open, refresh it…

  if ($window(@test1)) { close -@ @test1 | window -ak0hdBCpfo +d @test1 -1 -1 180 174 }

  ;else open it…

  else { window -ak0ChdBpfo +d @test1 -1 -1 180 174 }

  ;clear the picture window…

  clear @test1

  ;draw rectangle for window background...

  drawrect -rf @test1 $rgb(face) 1 0 0 180 174

  ;draw the 3d effect of window background…

  drawrect -rn @test1 $rgb(180,180,180) 1 0 0 180 174

  drawline -rn @test1 $rgb(255,255,255) 1 1 172 1 1 178 1

  drawline -rn @test1 $rgb(210,210,210) 1 1 172 178 172 178 1

  ;draw rectangle for listbox background...

  drawrect -nrf @test1 $rgb(243,240,234) 1 10 10 160 152

  ;draw 3d sunk in effect for listbox background…

  drawline -rn @test1 $rgb(200,200,200) 1 10 163 10 10 169 10

  drawline -rn @test1 $rgb(255,255,255) 1 10 163 169 163 169 11

  ;draw rectangle for listbox header...

  drawrect -rnf @test1 $rgb(233,230,224) 1 12 12 156 15

  ;draw the 3d effect for listbox header…

  drawrect -rn @test1 $rgb(200,200,200) 1 12 12 156 15

  drawline -rn @test1 $rgb(220,220,220) 1 13 25 166 25 166 13

  drawline -rn @test1 $rgb(255,255,255) 1 13 24 13 13 167 13

  ;draw the header title text, drawing twice with a lighter colour behind gives the text an engraved effect…

  ;the light text has to be offset by 1px on both the x and y co-ordinates…

  drawtext -rn @test1 $rgb(255,255,255) tahoma -7 21 15 Genre List

  drawtext -rn @test1 $rgb(160,160,160) tahoma -7 20 14 Genre List

  ;add a scrollbar for the listbox using the $scrollbar identifier....

  $scrollbar(@test1,156,28,12,134,$rgb(233,230,224),$rgb(233,230,224),$rgb(255,255,255),$rgb(200,200,200),@buffer,15)

  drawdot @test1

}

 

 

The alias above produces a window, which looks like the one below:-

 

This is just the bare skeleton of the listbox, as yet it doesn’t list data and it doesn’t allow for mouse events.

 

That’s all coming up…

 

At this stage you should have a listbox with a working scrollbar.

 

Remember that all colours and sizes are customisable.  You should just use the above alias as an example and play around with it, make bigger/smaller listbox’s.  It would be easier to make an identifier to draw the listbox skeleton, in which you can vary sizes by assigning x,y,w,h parameters and vary colours by assigning $rgb(…) values.  In a similar way to the $scrollbar(…) identifier.  However you would not need any window events for it, as each window requires its own unique coding.

 

 

 

 

This concludes this section of the tutorial, before moving on, you should ensure that you have created a listbox GUI that looks appealing to you, and that the scrollbar works as it should.  If it doesn’t go back and check the $scrollbar(…) identifier and ensure you have assigned all 11 parameters.

 

The Listing Process

 

This process involves the retrieving of data from the buffer window and displaying it onto the picture window.  As described earlier in the tutorial, the identifier that is most useful in retrieving data is $line(@window,N).  It allows you to retrieve text from a specific line of a buffer window.

 

The listing process involves a limited while loop, the limit would be the number of lines you wish to display at any given time, i.e. the MAX numbers of lines in the listbox.

 

The draw.buffer alias below, is an example of this limited while loop, the text drawn on the picture window has to be relevant to the position of the scrollbar thumb.  For this reason the position of the thumb has to be predetermined mathematically from the hash data of the scrollbar, in question.

 

It is necessary to have an alias like this for each picture window listbox you create, the downsides of this is are that it can be fairly time consuming and it does take up a fair amount of space.  This alias can be expanded upon, if for example you wish to create a listbox with multiple columns, you only need to add the data for the other columns into the same buffer window and add separate /drawtext commands into the same loop.

 

 

;descriptive breakdown of the listing process…

 

alias draw.buffer {

  ;declare variable %t, this allows you to set the starting point for the listing… e.g. draw.buffer 50, will list from line 50 onwards…

  var %t = $1

  ;retrieve hash data for the scrollbar, you know that there is only one scrollbar on the window, so there is no need to use a while loop…

  if ($hget(scrollbar. $+ @test1,1)) { tokenize 47 $ifmatch }

  ;if number of lines in buffer is less than MAX lines in list, and set variable to starting point…

  if ($line(@buffer,0) <= 10) set %genre.y $round($calc(($mouse.y - $2) / $4 * $line(@buffer,0)),0)

;if number of lines in buffer is more than MAX lines in list, do as above, however you have to subtract $calc(MAX lines – 1) from the number of lines in the buffer, in this case you subtract 9, highlighted below…

  elseif ($line(@buffer,0) > 10) { set %genre.y $ceil($calc(($mouse.y - $2) / $4 * $calc($line(@buffer,0) - 9)))) }

  ;if the starting point is 0, set it to 1 as there is no line 0 of text in the buffer window…

  if (%genre.y == 0) { set %genre.y 1 }

  ;if %t has been declared set the starting point to %t…

  if (%t) { set %genre.y $ifmatch }

  ;draw a rectangle of the same colour to the listbox background, so listbox text can be refreshed when scrolling…

  drawrect -rfn @test1 $rgb(243,240,234) 1 12 28 144 134

  ;if picture window is open, continue…

  if ($window(@test1)) {

  ;declare loop variables…

    var %x2 = %genre.y

    var %x = 1

  ;set up a LIMITING loop, here the limit is 10, i.e. this will list 10 lines in the picture window…

    while (%x <= 10) {

  ;check to ensure, that the buffer contains relevant text…

      if ($line(@buffer,%x2)) {

  ;draw text in a lighter colour, with a 1px offset, to create the engraved text effect…

        drawtext -rnc @test1 $rgb(255,255,255) "verdana" -7 18 $calc((13 * %x) + 18) 140 14 $line(@buffer,%x2)

;draw text by increasing the Y co-ordinate, this is done by using a multiplier, in this case it is 13.  Increasing this number would create a bigger space between lines of text, decreasing it would reduce the space…

;The text is retrieved using $line(@buffer,%x2), %x2 returns the line number…

        drawtext -rnc @test1 $rgb(140,140,140) "verdana" -7 17 $calc((13 * %x) + 17) 140 14 $line(@buffer,%x2)

      }

      inc %x

      inc %x2

    }

  ;drawdot refreshes the window, as –n flags are used in the /draw commands…

    drawdot @test1

  }

}

 

The listing process should be fairly straightforward, the biggest problem, is likely to be understanding how to calculate the Y co-ordinate of the text.  To calculate this, you need to use a multiplier value, so that the text is spaced evenly each time a line is added to the listbox.  In the above example the value 13 was used.  A constant value is also added, this is to determine the Y position in accordance to the listbox position and the header, in the above alias a value of 17 is added.

 

Let calculate the Y- position of the first line of text…

 

Y co-ordinate = $calc((13 * %x) + 17)

Y co-ordinate = $calc((13 * 1) + 17) … %x represents line number

Y co-ordinate = $calc(13 + 17)

 

Therefore Y co-ordinate for Line 1 of listbox text = 30

 

This process is carried out for each of the ten lines listed.

 

The result of the listing process is shown below:

 


On the /test1 alias which opens the listbox window, append the following two lines of text to the end of the alias, before the /drawdot command.  It displays the text and draws the scrollbar thumb in the correct position…

 

draw.buffer 1

draw.scroll @test1 1 $calc((%genre.y / $line(@buffer,0)) * 100)

 

%genre.y is the starting line (N) of text on the picture window.

 

 

 

 

 

 

 

This concludes the section of this tutorial on listing data from a buffer window to a picture window.  Before continuing, ensure that you are competent at creating listbox buffers and are able to retrieve data and draw onto a picture window.  Experiment, by increasing/decreasing Y co-ordinate multiplier values, and change the font to what ever you suits your needs, it can be changed by editing the “verdana” parameter in the /drawtext command.  The ……… are used so fonts which have spaces in the name can be used.  The font size is the –7, this can also be edit to suit your needs.

 

 

Mouse Event Handling

 

This is one of the more challenging tasks to complete as it can cause a lot of confusion, and does take a while to understand fully.

 

The object of the task is so that the listbox can have some actual function rather than, just be a utility to display data, mIRC has built in routines for mouse events, which can be used, however you may choose to use a dll, like band.dll by xhaker, to achieve the same thing.

 

The task involves, clicking on the text listed in the listbox and somehow highlighting it and also returning the text, i.e. in the form of an echo.  This is made difficult by the fact that the text co-ordinates are not stored values, i.e. they are unknown, so they must be calculated.  The example below does this, very accurately.

 

 

;descriptive breakdown of the mouse event handling…

 

menu @test1 {

  sclick: {

  ;check and ensure that the mouse event occurs within the listbox ONLY, and NOT over the header/scrollbar…

    if ($inrect($mouse.x,$mouse.y,12,28,144,132)) {

;as you need to calculate text position, you need to know the height of the text.  The letter T is used as it is only of the tallest letters in the alphabet, and as yet you can’t retrieve the actual text in the listbox…

      var %h = $calc($height(T,verdana,-7) + 1)

;the %p variable returns the line number of the @buffer window, on which the relevant text is…

;calculating %p is difficult to understand, however it involves determining the Y co-ordinate.  In the example below the value of 30 that is subtracted from $mouse.y is the same value we calculated for the Y co-ordinate of the first line of text, in the previous section of this tutorial…

;the value obtained is then divided by the height of the text, and returned as an integer value…

;clicking on the listbox should now return values of 1-10 depending on what line you click…

;the %genre.y is added so that the line values greater than 10 can be determined. (%genre.y is the starting line number of the list)…

;you can now return the text of lines by clicking on them, in the picture window listbox… /echo –s $line(@buffer,%p)

      var %p = $calc($int($calc(($mouse.y - 30) / %h)) + %genre.y)

;the %z variable is the same as %p, with one difference, it does not consider the %genre.y variable, so it therefore only returns numbers 1-10, this is important for highlighting purposes…

      var %z = $int($calc(($mouse.y - 30) / %h))

  ;now clicking on the lines in the picture window listbox will cause an echo of the line in the status window…

      echo -s sclick: $line(@buffer,%p)

  ;if %select variable is defined, continue…

      if (%select) {

  ;draw over previously selected line, with listbox background colours…

        drawreplace -nr @test1 $rgb(219,225,232) $rgb(243,240,234)14 %select 139 13

        drawrect -rn @test1 $rgb(243,240,234) 1 14 %select 139 13

      }

  ;this is where the calculations are made to highlight a certain row of the listbox…

  ;the %z value calculated above is multiplied by the SAME multiplier used in the listing process…

  ;then the Y co-ordinate for the first line of text in the listbox is added…

      set %select $calc((%z * 13) + 30)

  ;a coloured rectangle is drawn over the selected text…

      drawreplace -nr @test1 $rgb(243,240,234) $rgb(219,225,232) 14 $calc((%z * 13) + 30) 139 13

      drawrect -nr @test1 $rgb(157,167,177) 1 14 $calc((%z * 13) + 30) 139 13

      drawdot @test1

    }

  }

}

 

The above is just an example of an sclick event.  The same can be done for Dclick/Uclick/Rclick/Drop events, depending on what you want to use the list box for.

 

The result of the adding in mouse events for the listbox is shown below…

 

 

This is the final product, a fully functional listbox, which can be customised however you want.

 

 

 

 

 

 

 

 

 

 

 

 

 

Each time you create a new listbox, don’t forget to update the /scroll.update alias, other wise the scrollbar won’t work…

 

To run the example copy all of the example code above into your mIRC remote, copy the genre.txt file from the tutorial folder into the script directory, type /load.buffer in the active window editbox to create the buffer file.  Then add the two remaining lines of code as indicated in section 4, (by the GUI picture).  Following this, type /test1, in the active window editbox.  The above list should open up…

 

 

I hope this tutorial has been useful, in providing you with a better understand of how to create a listbox in a picture window, if you need any further assistance, or wish to report a code bug, or suggested improvements/additions please contact me using the information below.

 

 

 

Text Box: Author:	   Sanjay B
IRC:	    irc.elite-scriptaz.net
	   #elite-scriptaz / #picwin_help

Nickname:   Jynx / Jynx^

Email:	    sanj.s.b@gmail.com
Msn:	    jynx_scripter@msn.com